﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Newtonsoft.Json;

namespace ZilLion.Service.Core.Unities.UnitiesMethods
{
    /// <summary>
    ///     http请求帮助类
    /// </summary>
    public static class HttpHelper
    {
        #region x-www-form-urlencoded

        /// <summary>
        ///     产生application/x-www-form-urlencoded postbody(简单对象)
        /// </summary>
        /// <param name="bodyParams"></param>
        /// <param name="toUpper"></param>
        /// <returns></returns>
        public static string ConcatParams(Dictionary<string, string> bodyParams, bool toUpper = true)
        {
            var keys = bodyParams.Keys.ToArray().OrderBy(x => x);

            var str = string.Empty;

            foreach (var key in keys)
            {
                var val = bodyParams[key];
                if (toUpper)
                    str +=
                        "&" + key.UrlEncode(Encoding.UTF8) + "=" + val.UrlEncode(Encoding.UTF8).ToUpper();
                else
                    str +=
                        "&" + key.UrlEncode(Encoding.UTF8) + "=" + val.UrlEncode(Encoding.UTF8);
            }
            return str.ReplaceFirst("&", "");
        }

        /// <summary>
        ///     产生application/x-www-form-urlencoded postbody(简单对象)
        /// </summary>
        /// <param name="body"></param>
        /// <param name="toUpper"></param>
        /// <returns></returns>
        public static string ConcatParams<T>(T body, bool toUpper = true)
        {
            var dic = new Dictionary<string, string>();


            foreach (var prop in typeof(T).GetProperties())
                dic.Add(prop.Name, body.GetPropertyValue(prop.Name).ToString());
            return ConcatParams(dic, toUpper);
        }

        #endregion

        #region Put

        /// <summary>
        ///     http Post 请求
        /// </summary>
        /// var result = http.PutAsync(url, httpContent).Result;
        /// <typeparam name="T"></typeparam>
        /// <param name="url"></param>
        /// <param name="httpContent"></param>
        /// <returns></returns>
        public static T HttpRequestPut<T>(string url,
            HttpContent httpContent
        )
        {
            var deresponsejson = HttpRequestPut(url, httpContent);
            var resultdata = JsonConvert.DeserializeObject<T>(deresponsejson);
            return resultdata;
        }

        /// <summary>
        ///     Put
        /// </summary>
        /// <param name="url"></param>
        /// <param name="httpContent"></param>
        /// <returns></returns>
        public static string HttpRequestPut(string url,
            HttpContent httpContent
        )
        {
            using (var http = new HttpClient())
            {
                http.Timeout = new TimeSpan(0, 0, 0, 30);
                var result = http.PutAsync(url, httpContent).Result;
                if (result.StatusCode == HttpStatusCode.NotFound)
                    throw new Exception("服务访问404，请检查配置");
                var deresponsejson = result.Content.ReadAsStringAsync().Result;
                return deresponsejson;
            }
        }

        #endregion

        #region Get

        /// <summary>
        ///     http get 请求
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string HttpRequestGet(string url)
        {
            using (var http = new HttpClient())
            {
                http.Timeout = new TimeSpan(0, 0, 0, 30);
                var result = http.GetAsync(url).Result;
                if (result.StatusCode == HttpStatusCode.NotFound)
                    throw new Exception("服务访问404，请检查配置");
                return result.Content.ReadAsStringAsync().Result;
            }
        }

        /// <summary>
        ///     http get 请求
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="url"></param>
        /// <returns></returns>
        public static T HttpRequestGet<T>(string url)
        {
            var deresponsejson = HttpRequestGet(url);
            try
            {
                var resultdata = JsonConvert.DeserializeObject<T>(deresponsejson);
                return resultdata;
            }
            catch (Exception ex)
            {
                throw new Exception("解析失败。(deresponsejson:" + deresponsejson + ")", ex);
            }
        }

        /// <summary>
        ///     http get 请求
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="url"></param>
        /// <param name="getStrFunc"></param>
        /// <param name="customeRequestHeaders"></param>
        /// <returns></returns>
        public static T HttpRequestGet<T>(string url, Dictionary<string, string> customeRequestHeaders,
            Func<HttpContent, string> getStrFunc
        )
        {
            using (var http = new HttpClient())
            {
                http.Timeout = new TimeSpan(0, 0, 0, 30);
                if (customeRequestHeaders != null)
                    foreach (var herader in customeRequestHeaders)
                    {
                        if (http.DefaultRequestHeaders.Contains(herader.Key))
                            http.DefaultRequestHeaders.Remove(herader.Key);


                        http.DefaultRequestHeaders.Add(herader.Key, herader.Value);
                    }

                var result = http.GetAsync(url).Result;
                if (result.StatusCode == HttpStatusCode.NotFound)
                    throw new Exception("服务访问404，请检查配置");
                var deresponsejson = getStrFunc(result.Content);
                var lists = JsonConvert.DeserializeObject<T>(deresponsejson);
                return lists;
            }
        }

        #endregion

        #region Post

        /// <summary>
        ///     Put
        /// </summary>
        /// <param name="url"></param>
        /// <param name="httpContent"></param>
        /// <param name="customeRequestHeaders"></param>
        /// <param name="timeOut"></param>
        /// <param name="getStrFunc"></param>
        /// <returns></returns>
        public static string HttpRequestPost(string url,
            HttpContent httpContent, Dictionary<string, string> customeRequestHeaders = null, int timeOut = 30,
            Func<HttpContent, string> getStrFunc = null
        )
        {
            if (getStrFunc == null)
                getStrFunc += content => content.ReadAsStringAsync().Result;

            using (var http = new HttpClient())
            {
                if (customeRequestHeaders != null)
                    foreach (var herader in customeRequestHeaders)
                    {
                        if (http.DefaultRequestHeaders.Contains(herader.Key))
                            http.DefaultRequestHeaders.Remove(herader.Key);
                        http.DefaultRequestHeaders.Add(herader.Key, herader.Value);
                    }
                http.Timeout = new TimeSpan(0, 0, 0, timeOut);
                var result = http.PostAsync(url, httpContent).Result;
                if (result.StatusCode == HttpStatusCode.NotFound)
                    throw new Exception("服务访问404，请检查配置");

                var deresponsejson = getStrFunc(result.Content);
                return deresponsejson;
            }
        }


        /// <summary>
        ///     http Post 请求
        /// </summary>
        /// <param name="url"></param>
        /// <param name="request"></param>
        /// <param name="customeRequestHeaders"></param>
        /// <param name="timeOut"></param>
        /// <param name="getStrFunc"></param>
        /// <returns></returns>
        public static TResult HttpRequestPost<TRequest, TResult>(string url,
            TRequest request, Dictionary<string, string> customeRequestHeaders = null, int timeOut = 30,
            Func<HttpContent, string> getStrFunc = null
        )
        {
            HttpContent httpContent = new StringContent(JsonConvert.SerializeObject(request));
            httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            var deresponsejson = HttpRequestPost(url,
                httpContent, customeRequestHeaders, timeOut, getStrFunc);
            var resultdata = JsonConvert.DeserializeObject<TResult>(deresponsejson);
            return resultdata;
        }

        /// <summary>
        ///     http Post 请求
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="url"></param>
        /// <param name="httpContent"></param>
        /// <param name="customeRequestHeaders"></param>
        /// <param name="timeOut"></param>
        /// <param name="getStrFunc"></param>
        /// <returns></returns>
        public static T HttpRequestPost<T>(string url,
            HttpContent httpContent, Dictionary<string, string> customeRequestHeaders = null, int timeOut = 30,
            Func<HttpContent, string> getStrFunc = null
        )
        {
            var deresponsejson = HttpRequestPost(url,
                httpContent, customeRequestHeaders, timeOut, getStrFunc);
            var resultdata = JsonConvert.DeserializeObject<T>(deresponsejson);
            return resultdata;
        }

        #endregion

        #region 下载文件（支持断点续传）

        /// <summary>
        ///     下载文件
        /// </summary>
        /// <param name="url"></param>
        /// <param name="saveFilePath"></param>
        public static void DownloadFile(string url, string saveFilePath)
        {
            ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, errors) => true;
            //不验证服务端https证书

            var saveFileInfo = new FileInfo(saveFilePath);

            var file = saveFileInfo.Name;
            if (saveFileInfo.Directory == null) return;
            var dir = saveFileInfo.Directory.FullName;

            var tempsaveFilepath = Path.Combine(dir, file + ".packagetmp");


            using (var client = new HttpClient())
            {
                client.Timeout = new TimeSpan(0, 0, 0, 30);

                var result = client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead).Result;
                //只读httpheader即可返回，不用等待获取下载文件内容

                if (File.Exists(tempsaveFilepath))
                {
                    var targetFileinfo = new FileInfo(tempsaveFilepath);
                    if (result != null && targetFileinfo.Length >= result.Content.Headers.ContentLength)
                        File.Delete(tempsaveFilepath);
                }

                var downloadresult = DownloadFileWithResume(url, tempsaveFilepath);
                if (downloadresult)
                {
                    var fileinfo = new FileInfo(tempsaveFilepath);
                    fileinfo.Rename(saveFileInfo.Name); //把tmp 后缀名文件重新改为正常文件名
                }
            }
        }

        /// <summary>
        ///     断点续传下载
        /// </summary>
        /// <param name="sourceUrl"></param>
        /// <param name="destinationPath"></param>
        private static bool DownloadFileWithResume(string sourceUrl, string destinationPath)
        {
            try
            {
                long existLen = 0;
                FileStream saveFileStream;
                if (File.Exists(destinationPath))
                {
                    var fInfo = new FileInfo(destinationPath);
                    existLen = fInfo.Length; //已下载流长度
                }
                if (existLen > 0) //如果文件存在则FileMode.Append 否则Create
                    saveFileStream = new FileStream(destinationPath,
                        FileMode.Append, FileAccess.Write,
                        FileShare.ReadWrite);
                else
                    saveFileStream = new FileStream(destinationPath,
                        FileMode.Create, FileAccess.Write,
                        FileShare.ReadWrite);


                var httpWebRequest = (HttpWebRequest) WebRequest.Create(sourceUrl);
                httpWebRequest.AddRange((int) existLen);
                var httpWebResponse = (HttpWebResponse) httpWebRequest.GetResponse();
                using (var respStream = httpWebResponse.GetResponseStream())
                {
                    var timout = httpWebRequest.Timeout;
                    respStream?.CopyTo(saveFileStream);
                }
                saveFileStream.Close();
                saveFileStream.Dispose();
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        #endregion

        #region 上传文件

        /// <summary>
        ///     上传文件
        /// </summary>
        /// <param name="urlpath"></param>
        /// <param name="filePath"></param>
        public static T UpLoadFile<T>(string urlpath, string filePath, Func<HttpContent, string> getStrFunc = null)
        {
            if (getStrFunc == null)
                getStrFunc += content => content.ReadAsStringAsync().Result;
            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/json")); //设定要响应的数据格式
                using (var content = new MultipartFormDataContent()) //表明是通过multipart/form-data的方式上传数据
                {
                    var file = GetFileByteArrayContent(filePath); //获取文件集合对应的ByteArrayContent集合
                    Action<ByteArrayContent> act = byteArrayContent =>
                    {
                        //声明一个委托，该委托的作用就是将ByteArrayContent集合加入到MultipartFormDataContent中

                        content?.Add(byteArrayContent);
                    };

                    act(file); //执行act
                    try
                    {
                        var result = client.PostAsync(urlpath, content).Result; //post请求

                        if (result.StatusCode == HttpStatusCode.NotFound)
                            throw new Exception("服务访问404，请检查配置");
                        var deresponsejson = getStrFunc(result.Content);
                        var resultobj = JsonConvert.DeserializeObject<T>(deresponsejson);

                        return resultobj;
                    }
                    catch (Exception)
                    {
                        return default(T);
                    }
                }
            }
        }

        /// <summary>
        ///     获取文件集合对应的ByteArrayContent集合
        /// </summary>
        /// <returns></returns>
        private static ByteArrayContent GetFileByteArrayContent(string file)
        {
            var fileContent = new ByteArrayContent(File.ReadAllBytes(file));
            fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
            {
                FileName = Path.GetFileName(file)
            };

            return fileContent;
        }
    }

    #endregion
}